﻿using System;
using System.Collections.Generic;
using com.yoozoo.gta.Gameplay.RTS;
using UnityEngine;
using Yoozoo.Gameplay.RTS.Proto;
using Yoozoo.Libs;

namespace Yoozoo.Gameplay.RTS
{
    // 行军部队的路径
    // 路径有多个点组成，有可能没有路径，说明该部队处于停止状态
    // 有路径的情况，根据
    // a. 服务器时间和本地时间，
    // b. 行军速度，
    // 计算
    // a. 当前位置和所在的线段，并将信息同步到Segment类中
    // b. 走完当前所在线段所需的时间，用于下一次计算
    // 路径还用于描绘行军路线

    public class TroopPath : IClass
    {
        /// <summary>
        /// 上一次服务器同步时间，单位：秒
        /// </summary>
        public long LastServerTime;

        /// <summary>
        /// 上一次服务器同步时的位置
        /// </summary>
        public Vector3 ServerPosition;

        private Vector3 _serverPosition;
        private Vector3 _clentPosition;
        public bool IsBattleMarch;
        private bool isInBattle;
        public bool NeedUpdateLine;
        /// <summary>
        /// 路点列表，从服务器同步而来
        /// 1. 服务器的数据结构为Coordinate，需要转成Vector3
        /// 2. 已经走过的路点，服务器不再发送，所以路点数量会越来越少
        /// 3. 极限时路点数量会达到100+，无法使用固定大小的数组保存
        /// 4. 预先分配16个，减少GC.Alloc
        /// </summary>
        public List<Vector3> Points = new List<Vector3>(16);
        //
        public float Speed;

        public int SegmentIndex;

        public bool isMoving;

        private long nextSegmentTime = 0;

        /// <summary>
        /// 路径总长度
        /// </summary>
        private float totalDistance;

        /// <summary>
        /// 剩余长度
        /// </summary>
        private float remainDistance;

        public bool isServerPosChange;
        private long _uid;
        public bool IsForceMoveChild = false;

        public Vector3 ClientPosition
        {
            get => _clentPosition;
            set
            {
                _clentPosition = value;
                //Debug.Log($"{_uid}-TroopPathx  frameCount： {Time.frameCount}  clientPos = {_clentPosition}  ");
            }
        }

        public void UpdateInfo(RtsMapMonster monster,ref Segment segment)
        {
            var lod3 = monster.lod3;
            _UpdateInfo(lod3.npcUid, lod3.lastFrameTime, lod3.pathList, lod3.coordinate, lod3.speed, ref segment);
        }

        private bool isBattleMarch(TargetCoordinateType targetType)
        {
            return targetType == TargetCoordinateType.CASTLE_2 || 
                   targetType == TargetCoordinateType.MARCH_3 ||
                   targetType == TargetCoordinateType.MONSTER_4 ||
                   targetType == TargetCoordinateType.ALLIANCE_CASTLE_10 ||
                   targetType == TargetCoordinateType.ALLIANCE_FLAG_11 ;
        }
        public void UpdateInfo(RtsServerInfo rtsServerInfo,ref Segment segment,bool forceRefresh = false)
        {
            IsForceMoveChild = false;
            if (rtsServerInfo.pathList != null && !forceRefresh)
            {
                var serverPathLenth = rtsServerInfo.pathList.Length;
                var currentPathLength = Points.Count;
                if (serverPathLenth > 0 && currentPathLength > 0)
                {
                    // 防止路点未变重复创建线路，
                    // 解决导致客户端路径和服务器路当前坐标不一致问题(lod更改时)
                    if (rtsServerInfo.pathList[serverPathLenth - 1].ToVector3() == Points[currentPathLength - 1] && Math.Abs(rtsServerInfo.speed - Speed*3600) < 0.01f) 
                    {
                        return;
                    }
                }
            }

            this.NeedUpdateLine = true;
            IsBattleMarch = isBattleMarch(rtsServerInfo.targetType);
            isInBattle = rtsServerInfo.marchStatus == MarchStatus.FIGHT_2 || rtsServerInfo.marchStatus == MarchStatus.SIEGE_FIGHT_6;
            _UpdateInfo(rtsServerInfo.tid, rtsServerInfo.lastFrameTime, rtsServerInfo.pathList, rtsServerInfo.coordinate, rtsServerInfo.speed, ref segment);
        }
        
        private void _UpdateInfo(long tid,long lastFrameTime, Coordinate[] pathList, Coordinate curServerPos,float speed,ref Segment segment)
        {
            this._uid = tid;
         
            LastServerTime = lastFrameTime;
            Points.Clear();
            if (pathList == null || pathList.Length==0)
            {
                isMoving = false;
                
            }
            else
            {
                foreach (var coordinate in pathList)
                {
                    Points.Add(coordinate.ToVector3());
                }

                isMoving = true;
            }

            if (segment._uid == 0 || segment._uid!=this._uid)  // 是否第一次同步部队位置,或者被重用
            {
                ClientPosition = curServerPos.ToVector3();
                ServerPosition = ClientPosition;
                if (segment._uid!=0 && segment._uid != this._uid)
                {
                    Debug.Log($"重用了segment {this._uid}  olduid = {segment._uid} ");
                }
            }
            else
            {
               
                var diff = Vector3.Distance(segment.clientPosition, curServerPos.ToVector3());
                isServerPosChange = diff>0.2f; //当前服务器的点和上一次的服务器的点; 用来做追击的；服务器没给路线，但是位置变了
                // 将当前点置为客户端当前点
                ClientPosition = segment.clientPosition;
                IsForceMoveChild = diff > MarchSequareConstant.DeviationDistance;
                /*if (tid == "2645374426701669")
                {
                    Debug.LogError("2645374426701669 "+ isMoving + " " + diff + " " + isInBattle);
                }*/
                // Debug.Log($"{tid}-TroopPathxxxx isServerPosChange{isServerPosChange}  frameCount： {Time.frameCount}  clientPos = {ClientPosition}   localserverPos: {ServerPosition} newServerPos:{curServerPos.ToVector3()} ");
                if (diff > MarchSequareConstant.DeviationDistance)
                {
                    ClientPosition = curServerPos.ToVector3();
                    ServerPosition = ClientPosition;
                }
                else if (isMoving)  //因为和服务器坐标不一致，这时候再次拖拽部队，会跳，所以这里修正坐标，用客户端的坐标为起始点
                {
                    Points[0] = ClientPosition;
                    ClientPosition = curServerPos.ToVector3();
                    ServerPosition = ClientPosition;
                }
                else if (isInBattle)
                {
                    ServerPosition = curServerPos.ToVector3();
                }
                else //用客户端的当前坐标作为起始点； 这个是了矫正驻扎问题: 点击驻扎、服务返回的部队坐标跟客户端有差异，比客户端慢，为了表现就让部队用当前坐标； 
                {
                    ServerPosition = ClientPosition;
                }
            }
         
            
            // 服务器坐标是否改变，只在战斗中位置移动追击用
            //Debug.Log($"{troopMarch.lod2.tid}-TroopPath  isServerPosChange{isServerPosChange} frameCount： {Time.frameCount}  clientPos = {ClientPosition}   serverPos: {ServerPosition}");
            if (speed != 0)
            {
                Speed = speed / 3600;
            }
            else //TODO:不移动服务器不给野怪的速度。  这里默认用上一个状态的，否则就需要读表，然后。。取最小的值
            {
                Speed = Speed==0?350.0f/3600 : Speed;
            }
            
            SegmentIndex = 0;
            long clientTime = TimeUtils.GetServerTimeMs();
            //Debug.LogErrorFormat("[{4}]LastServerTime: {0}, clientTime: {1}, delta: {2}, ServerPosition: {3}",
            //    LastServerTime, clientTime, (clientTime - LastServerTime) / 1000f, ServerPosition, Time.frameCount);
            Calculate(LastServerTime, clientTime, ServerPosition);


            UpdateSegment(ref segment);
        }
       

        /// <summary>
        ///  计算当前位置和当前路段
        /// </summary>
        /// <param name="previousTime"></param>
        /// <param name="currentTime"></param>
        /// <param name="previousPosition"></param>
        public void Calculate(long previousTime, long currentTime, Vector3 previousPosition)
        {
            // 非移动状态不需要更新路段
            if (!isMoving)
                return;

            //又拿到一秒？
            float elapsedTime = (currentTime - previousTime) / 1000.0f;
            if (elapsedTime < 0)
            {
                elapsedTime = 0.009f;
            }
            var startPosition = previousPosition;
            if (SegmentIndex + 1 >= Points.Count)
            {
                return;
            }
            var endPosition = Points[SegmentIndex +1];

            for(;;)
            {
                var vector = endPosition - startPosition;
                float distance = vector.magnitude;
                float costTime = distance / Speed;
               
                if (elapsedTime > costTime)//时间超过这一段
                {
                    elapsedTime -= costTime;
                    if (Points.Count > SegmentIndex + 2)
                    {
                        SegmentIndex++;
                        startPosition = endPosition;
                        endPosition = Points[SegmentIndex +1];
                    }
                    else
                    {
                        ClientPosition = endPosition;
                        isMoving = false;
                        break;
                    }
                }
                else
                {
                    if (costTime != 0) 
                    {
                        ClientPosition =  startPosition + vector * elapsedTime / costTime;//这个就是当前应该在的点
                        nextSegmentTime = currentTime + (long)((costTime - elapsedTime) * 1000);//TODO:这里会有误差吧
                    } 
                    else  // 说明两个路点相等了
                    {
                        ClientPosition = startPosition;
                        nextSegmentTime = currentTime;
                    }
                 
                   
                    break;
                }
            }
        }

        /// <summary>
        /// 检查是否到了下一路段
        /// </summary>
        /// <param name="serverTime"></param>
        public bool UpdateNextSegment(long serverTime)
        {
            if (nextSegmentTime > serverTime)
            {
                return false;
            }
            //TODO:这里为什么>=,倒数第二个点就不更新了吗？
            if (SegmentIndex + 2 >= Points.Count)
            {
                isMoving = false;
                return false;
            }

            //Debug.LogErrorFormat("[{2}:{3}]NextSegment: {0} > {1}", nextSegmentTime, serverTime, Time.frameCount, serverTime);

            SegmentIndex++;
            
            Calculate(nextSegmentTime, serverTime, Points[SegmentIndex]);
            return true;
        }
        
        public void OnReset()
        {
            LastServerTime = 0;
            nextSegmentTime = 0;
            ServerPosition = Vector3.zero;
            IsBattleMarch = false;
            Speed = 0;
            Points.Clear();
            _uid = 0;
        }

        public void UpdateSegment(ref Segment segment)
        {
            var currentPosition = ClientPosition;
            var startPosition = currentPosition;
            var endPosition = currentPosition;
            if (isMoving)
            {
                if (SegmentIndex + 1 < Points.Count)
                {
                    startPosition = Points[SegmentIndex];
                    endPosition = Points[SegmentIndex + 1];
                }
            }
            else
            {
                if (isServerPosChange || segment._uid == 0)
                {
                    ClientPosition = ServerPosition;
                    startPosition = ServerPosition;
                    endPosition = ServerPosition;
                }
                else
                {
                    //如果pos没更新或者误差很小，那么就用当前客户端的位置
                    ClientPosition = segment.clientPosition;
                    startPosition = ClientPosition;
                    endPosition = ClientPosition;
                }
            }

            segment._uid = this._uid;
            segment.UpdateInfo(ClientPosition, startPosition, endPosition, Speed, LastServerTime,SegmentIndex+2==Points.Count,IsBattleMarch);
            
        }
    }
}
